#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>

#ifdef RT_USING_SERIAL

#include "board.h" 
#include "HAL_conf.h"

struct device_uart
{
    struct rt_serial_device serial;

    UART_TypeDef *instance;
    enum IRQn irqno;
};

static struct device_uart uart1;

static int uart_putc(struct rt_serial_device *serial, char c)
{
    struct device_uart *uart = serial->parent.user_data;

    RT_ASSERT(serial != RT_NULL);

    while ((uart->instance->CSR & 0x1) == 0)
        ;
    uart->instance->TDR = c;

    //return uart_write_byte(uart->port, c);
    return 1;
}

static int uart_getc(struct rt_serial_device *serial)
{
    struct device_uart *uart = serial->parent.user_data;
    int ch = -1;

    RT_ASSERT(serial != RT_NULL);

    if (uart->instance->CSR & (1 << 1))
    {
        ch = uart->instance->RDR;
    }

    return ch;
}

static rt_size_t uart_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction)
{
    return (0);
}

static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    return RT_EOK;
}

static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct device_uart * uart;

    uart = serial->parent.user_data;

    RT_ASSERT(uart != RT_NULL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        // rt_kprintf("NVIC_DisableIRQ(uart->irqno)=%d\n", uart->irqno);
        NVIC_DisableIRQ(uart->irqno);
        break;

    case RT_DEVICE_CTRL_SET_INT:
        // rt_kprintf("NVIC_EnableIRQ(uart->irqno)=%d\n", uart->irqno);
        NVIC_EnableIRQ(uart->irqno);
        break;
    }

    return (RT_EOK);
}

static const struct rt_uart_ops _uart_ops =
{
    uart_configure,
    uart_control,
    uart_putc,
    uart_getc,
    uart_dma_transmit
};

static void UartInit(UART_TypeDef *UARTx, int BaudRate)
{
    UART_InitTypeDef UART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //uart1_tx  PA9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 推免复用输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //uart1_rx  PA10
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOA, GPIO_Pin_9 | GPIO_Pin_10, GPIO_AF_UART_1); //PA9、PA10复用为串口1
    GPIO_PinAFConfig(GPIOA, GPIO_Pin_15, GPIO_AF_GPIO);                          //PA15复用为普通GPIO

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);
    UART_InitStructure.UART_BaudRate = BaudRate;             //波特率
    UART_InitStructure.UART_WordLength = UART_WordLength_8b; //数据位
    UART_InitStructure.UART_StopBits = UART_StopBits_1;      //停止位
    UART_InitStructure.UART_Parity = UART_Parity_No;
    UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; //输入输出模式
    UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
    UART_Init(UARTx, &UART_InitStructure);
    UART_Cmd(UARTx, ENABLE); //UART 模块使能

    UART_ClearITPendingBit(UARTx, 0xff);

	UART_ITConfig(UARTx, UART_IT_RXIEN, ENABLE);
	NVIC_SetPriority(UART1_IRQn, 3);
}

int rt_hw_uart_init(void)
{
    struct rt_serial_device *serial;
    struct device_uart      *uart;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

#if 1
//#ifdef BEKEN_USING_UART1
    {
        serial  = &uart1.serial;
        uart    = &uart1;

        serial->ops              = &_uart_ops;
        serial->config           = config;
        serial->config.baud_rate = 115200;
        serial->config.bufsz     = 2048;

        uart->instance = UART1;
        uart->irqno = UART1_IRQn;

        UartInit(UART1, 460800);

        rt_hw_serial_register(serial,
                              "uart1",
                              RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                              uart);
    }
#endif

    return 0;
}

void UART1_IRQHandler(void)
{
    struct device_uart *uart;

    uart = &uart1;

    if (uart->instance->ISR & (1 << 1))
    {
        rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_RX_IND);

        uart->instance->ICR |= 1 << 1;
    }
}
#endif /* RT_USING_SERIAL */
